<template>
  <PageWrapper>
    <!-- 笔记 -->
    <CollapseContainer class="mt-5" title="文档">
      <markdown-viewer :value="getDoc()" />
    </CollapseContainer>

    <CollapseContainer class="mt-5" title="响应式-基本类型">
      <ol>
        <li>字符串:{{ str }}<Button @click="str += '~'">修改</Button></li>
        <li>布尔类型:{{ b }}<Button @click="b = !b">修改</Button></li>
        <li>数字类型:{{ n }}<Button @click="n += 1">修改</Button></li>
      </ol>
      <template #action>
        <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('基本类型', getBasic())" />
      </template>
    </CollapseContainer>

    <CollapseContainer class="mt-5" title="响应式-对象类型">
      <ol>
        <li>姓名:{{ person.name }}<Button @click="changeName">修改姓名</Button></li>
        <li>年龄:{{ person.age }}<Button @click="changeAge">修改年龄</Button></li>
        <li>对象:{{ person }}<Button @click="changePerson">修改对象</Button></li>
      </ol>
      <template #action>
        <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('对象类型', getObject())" />
      </template>
    </CollapseContainer>

    <CollapseContainer class="mt-5" title="响应式-数组类型">
      <ol>
        <li>
          字符串数组:{{ JSON.stringify(strArray) }}
          <Button @click="addstr">添加</Button>
          <Button @click="deleteStr">删除</Button>
          <Button @click="clearStr">清空</Button>
        </li>
        <li>
          对象数组:{{ personArray }}
          <Button @click="addPerson">添加</Button>
          <Button @click="deletePerson">删除</Button>
          <Button @click="clearPerson">清空</Button>
        </li>
      </ol>
      <template #action>
        <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('数组类型', getArray())" />
      </template>
    </CollapseContainer>

    <CollapseContainer class="mt-5" title="响应式-响应失效">
      <ol>
        <li>person.name:{{ person.name }}<Button @click="changeName">修改</Button></li>
        <li>同步:nameRef:{{ nameRef }}<Button @click="changeNameRef">修改</Button></li>
        <li>失效:nameGet:{{ nameGet }}<Button @click="changeNameGet">修改</Button></li>
        <li>失效:nameLet:{{ nameLet }}<Button @click="changeNameLet">修改</Button></li>
        <li>
          person.name 与 nameRef 同步修改。nameGet 和 nameLet 修改后，不具有响应式。 但当原对象 person.name
          进行变动后，会将 nameGet 和 nameLet 的最新的值同步到页面上。
        </li>
      </ol>
      <template #action>
        <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('响应失效', getInvalid())" />
      </template>
    </CollapseContainer>

    <CollapseContainer class="mt-5" title="响应式-深度引用">
      <ol>
        <li>城市:{{ address.city }}<Button @click="changeCity">修改</Button></li>
        <li>坐标x:{{ address.location.x }}<Button @click="changeLocationX">修改</Button></li>
        <li>坐标y:{{ address.location.y }}<Button @click="changeLocationY">修改</Button></li>
        <li>地址:{{ address }}<Button @click="changeAddress">修改</Button></li>
      </ol>
      <template #action>
        <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('深度引用', getDeep())" />
      </template>
    </CollapseContainer>

    <!-- 源码组件 -->
    <SourceDrawer ref="sourceDrawer" />
  </PageWrapper>
</template>
<script lang="ts" setup>
  import { CollapseContainer } from '@/components/Container';
  import { MarkdownViewer } from '@/components/Markdown';
  import { PageWrapper } from '@/components/Page';
  import { Button } from '@/components/Button';
  import SourceDrawer from '@/learn/SourceDrawer.vue';
  import { ref, toRefs } from 'vue';
  import { Icon } from '@/components/Icon';

  // 定义子组件引用
  let sourceDrawer = ref<any>(null);

  function openSource(title: string, content: string) {
    sourceDrawer.value.open({ title, content, width: '60%' });
  }

  let person = ref({
    name: 'zhangsan',
    age: 20,
  });

  function changeName() {
    person.value.name += '~';
  }
  function changeAge() {
    person.value.age += 1;
  }
  function changePerson() {
    // 可以直接使用整个对象替换，新对象有什么属性就保留什么属性
    person.value = { name: 'lisi', age: 22 };
  }

  // 基本类型
  let b = ref(true);
  let str = ref('');
  let n = ref(0);

  // 数组类型
  let strArray = ref<string[]>([]);
  let personArray = ref<any>([]);

  function addstr() {
    strArray.value.push('a');
  }
  function deleteStr() {
    strArray.value.pop();
  }
  function clearStr() {
    // 清空，直接修改value为空数组, 依然是响应式对象
    strArray.value = [];
  }
  function addPerson() {
    personArray.value.push({ name: 'zhangsan', age: 23 });
  }
  function deletePerson() {
    personArray.value.shift();
  }
  function clearPerson() {
    // 清空，对象形式也可以直接修改value为空数组, 依然是响应式对象
    personArray.value = [];
  }

  // 响应失效
  let nameGet = person.value.name;
  // 解构会使对象使用响应特性。解构的同时，进行重命名
  let { name: nameLet } = person.value;
  // toRefs 支持解构多个对象时，toRef 只支持单个对象
  let { name: nameRef } = toRefs(person.value);

  function changeNameGet() {
    nameGet += '-get';
  }
  function changeNameLet() {
    nameLet += '-let';
  }
  function changeNameRef() {
    nameRef.value += '-ref';
  }

  // 深度引用
  let address = ref({ city: '北京', location: { x: 100, y: 200 } });

  function changeCity() {
    address.value.city += '~';
  }
  function changeLocationX() {
    address.value.location.x += 10;
  }
  function changeLocationY() {
    address.value.location.y += 20;
  }
  function changeAddress() {
    let newCity = { city: '上海', location: { x: 200, y: 400 } };
    address.value = newCity;
  }
</script>

<script lang="ts">
  function getDoc() {
    return `
### 核心特点
* ref 可用于基本类型、对象类型、数组类型
* ref 会将原对象封装为 RefImpl 对象，原对象作为其 value 属性
* ref 支持对象本身替换，即地址替换。替换后依然具有响应式，因为页面组件持有的是 RefImpl 的地址引用，而非原对象的地址引用
* 解构语法相当于赋值，新生成的对象不具有实时响应性，需要通过 toRef 或 toRefs 修饰为响应对象。

### 基本语法
* 引入 ref 对象: import { ref } from 'vue';
* 定义响应式对象:  let b = ref(true); let obj = ref({});
* ts/js 中使用，需要通过.value 属性:  $ref.value = xxx
* 模板中使用，不需要添加 .value 属性: {{ $ref }}
* 解构语法: let { name } = toRef(person.value); let { name, age } = toRefs(person.value);
`;
  }

  function getBasic() {
    return `
\`\`\`html
<template>
<CollapseContainer class="mt-5" title="响应式-基本类型">
  <ol>
    <li>字符串:{{ str }}<Button @click="str += '~'">修改</Button></li>
    <li>布尔类型:{{ b }}<Button @click="b = !b">修改</Button></li>
    <li>数字类型:{{ n }}<Button @click="n += 1">修改</Button></li>
  </ol>
  <template #action>
    <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('对象引用', getBasic())" />
  </template>
</CollapseContainer>
</template>

\<script lang="ts" setup\>
  import { ref } from 'vue';
  // 基本类型, ref 变量不需要使用 :ref 指定类型
  let b = ref(true);
  let str = ref('');
  let n = ref(0);
\</script\>
\`\`\`
`;
  }
  function getObject() {
    return `
\`\`\`html
<template>
  <CollapseContainer class="mt-5" title="响应式-对象类型">
    <ol>
      <li>姓名:{{ person.name }}<Button @click="changeName">修改姓名</Button></li>
      <li>年龄:{{ person.age }}<Button @click="changeAge">修改年龄</Button></li>
      <li>对象:{{ person }}<Button @click="changePerson">修改对象</Button></li>
    </ol>
    <template #action>
      <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('对象引用', getObject())" />
    </template>
  </CollapseContainer>
</template>

\<script lang="ts" setup\>
  import { ref } from 'vue';

  let person = ref({
    name: 'zhangsan',
    age: 20,
  });

  function changeName() {
    person.value.name += '~';
  }
  function changeAge() {
    person.value.age += 1;
  }
  function changePerson() {
    // 可以直接使用整个对象替换，新对象有什么属性就保留什么属性
    person.value = { name: 'lisi', age: 22 };
  }
\</script\>
\`\`\`
`;
  }
  function getArray() {
    return `
\`\`\`html
<template>
  <CollapseContainer class="mt-5" title="响应式-数组">
    <ol>
      <li>
        字符串数组:{{ JSON.stringify(strArray) }}
        <Button @click="addstr">添加</Button>
        <Button @click="deleteStr">删除</Button>
        <Button @click="clearStr">清空</Button>
      </li>
      <li>
        对象数组:{{ personArray }}
        <Button @click="addPerson">添加</Button>
        <Button @click="deletePerson">删除</Button>
        <Button @click="clearPerson">清空</Button>
      </li>
    </ol>
    <template #action>
      <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('对象引用', getObject())" />
    </template>
  </CollapseContainer>
</template>

\<script lang="ts" setup\>
  import { ref } from 'vue';

  // 数组类型
  let strArray = ref<string[]>([]);
  let personArray = ref<any>([]);

  function addstr() {
    strArray.value.push('a');
  }
  function deleteStr() {
    strArray.value.pop();
  }
  function clearStr() {
    // 清空，直接修改value为空数组, 依然是响应式对象
    strArray.value = [];
  }
  function addPerson() {
    personArray.value.push({ name: 'zhangsan', age: 23 });
  }
  function deletePerson() {
    personArray.value.shift();
  }
  function clearPerson() {
    // 清空，对象形式也可以直接修改value为空数组, 依然是响应式对象
    personArray.value = [];
  }
\</script\>
\`\`\`
`;
  }
  function getInvalid() {
    return `
\`\`\`html
<template>
  <CollapseContainer class="mt-5" title="响应式-响应失效">
    <ol>
      <li>person.name:{{ person.name }}<Button @click="changeName">修改</Button></li>
      <li>同步:nameRef:{{ nameRef }}<Button @click="changeNameRef">修改</Button></li>
      <li>失效:nameGet:{{ nameGet }}<Button @click="changeNameGet">修改</Button></li>
      <li>失效:nameLet:{{ nameLet }}<Button @click="changeNameLet">修改</Button></li>
      <li>
        person.name 与 nameRef 同步修改。nameGet 和 nameLet 修改后，不具有响应式。 但当原对象 person.name
        进行变动后，会将 nameGet 和 nameLet 的最新的值同步到页面上。
      </li>
    </ol>
    <template #action>
      <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('响应失效', getInvalid())" />
    </template>
  </CollapseContainer>
</template>

\<script lang="ts" setup\>
  import { reactive, toRef } from 'vue';

  let person = reactive({
    name: 'zhangsan',
    age: 20,
  });

  // 响应失效
  let nameGet = person.value.name;
  // 解构会使对象使用响应特性。解构的同时，进行重命名
  let { name: nameLet } = person.value;
  // toRefs 支持解构多个对象时，toRef 只支持单个对象
  let { name: nameRef } = toRefs(person.value);

  function changeNameGet() {
    nameGet += '-get';
  }
  function changeNameLet() {
    nameLet += '-let';
  }
  function changeNameRef() {
    nameRef.value += '-ref';
  }
\</script\>
\`\`\`
`;
  }

  function getDeep() {
    return `
\`\`\`html
<template>
  <CollapseContainer class="mt-5" title="响应式-深度引用">
    <ol>
      <li>城市:{{ address.city }}<Button @click="changeCity">修改</Button></li>
      <li>坐标x:{{ address.location.x }}<Button @click="changeLocationX">修改</Button></li>
      <li>坐标y:{{ address.location.y }}<Button @click="changeLocationY">修改</Button></li>
      <li>地址:{{ address }}<Button @click="changeAddress">修改</Button></li>
    </ol>
    <template #action>
      <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('深度引用', getDeep())" />
    </template>
  </CollapseContainer>
</template>

\<script lang="ts" setup\>
  import { reactive } from 'vue';

  // 深度引用
  let address = ref({ city: '北京', location: { x: 100, y: 200 } });

  function changeCity() {
    address.value.city += '~';
  }
  function changeLocationX() {
    address.value.location.x += 10;
  }
  function changeLocationY() {
    address.value.location.y += 20;
  }
  function changeAddress() {
    let newCity = { city: '上海', location: { x: 200, y: 400 } };
    address.value = newCity;
  }
\</script\>
\`\`\`
`;
  }
</script>

<style lang="less" scoped>
  button {
    margin-left: 5px;
  }
</style>
